/*
 * Copyright (c) 2016, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

//****************************************************************************
//
//! \addtogroup
//! @{
//
//****************************************************************************
/* Standard Include */
//#include <ap_provisioning.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <mqueue.h>
#include <time.h>
#include <stdbool.h>

/* TI-DRIVERS Header files */
#include <ti/drivers/net/wifi/simplelink.h>
#include <ti/drivers/GPIO.h>
#include <ti/display/Display.h>
#include <ti/drivers/Timer.h>
#include <ti/sbl/sbl.h>
#include <ti/sbl/sbl_image.h>
#include <ble_provisioning.h>
#include <ti/devices/cc32xx/inc/hw_ints.h>
#include <ti/devices/cc32xx/inc/hw_memmap.h>
#include <ti/devices/cc32xx/inc/hw_types.h>
#include <ti/devices/cc32xx/driverlib/rom.h>
#include <ti/devices/cc32xx/driverlib/rom_map.h>
#include <ti/devices/cc32xx/driverlib/prcm.h>

/* Local Definitions */
#include "Profile/ble_wifi_provision_profile.h"
#include "Profile/profile_util.h"
#include "sbl_wifi_snp_update.h"

#include "wifi_doorlock_app.h"
#include "Board.h"

/*******************************************************************************
 Function Prototypes
 ******************************************************************************/
static void BLE_SPWriteCB(uint8_t charID);
static void BLE_SPcccdCB(uint8_t charID, uint16_t value);
static void BLE_asyncCB(uint8_t cmd1, void *pParams);
static void BLE_initServices(void);
static void BLE_processSNPEventCB(uint16_t event, snpEventParam_t *param);

/*******************************************************************************
 Variables
 ******************************************************************************/
/* Used for log messages */
extern Application_CB App_CB;
extern Display_Handle display;

/* Communication queues */
extern mqd_t bleQueueRec;
extern mqd_t bleQueueSend;

static BLEProfileCallbacks_t bleCallbacks = { BLE_SPWriteCB, BLE_SPcccdCB };
static char nwpstr[] = "NWP:  0xFFFFFFFFFFFF";
static char peerstr[] = "Peer: 0xFFFFFFFFFFFF";

/* Connection Handle - Only one device currently allowed to connect to SNP */
static uint16_t connHandle = BLE_DEFAULT_CONN_HANDLE;

/* Device Name */
static uint8_t snpDeviceName[] = { 'C', 'C', '3', '2', 'X', 'X', ' ', 'W', 'i',
                                   'F', 'i' };

/* GAP - SCAN RSP data (max size = 31 bytes) */
static uint8_t scanRspData[] = {
/* Complete Name */
0x0C,/* length of this data */
SAP_GAP_ADTYPE_LOCAL_NAME_COMPLETE, 'C', 'C',
'3', '2', 'X', 'X', ' ', 'W', 'i', 'F', 'i',

/* Connection interval range */
0x05, /* length of this data */
0x12, /* GAP_ADTYPE_SLAVE_CONN_INTERVAL_RANGE, */
LO_UINT16(DEFAULT_DESIRED_MIN_CONN_INTERVAL),
HI_UINT16(DEFAULT_DESIRED_MIN_CONN_INTERVAL),
LO_UINT16(DEFAULT_DESIRED_MAX_CONN_INTERVAL),
HI_UINT16(DEFAULT_DESIRED_MAX_CONN_INTERVAL),
0x02, /* length of this data */
0x0A, /* GAP_ADTYPE_POWER_LEVEL, */
0 /* 0dBm */
};

/* GAP - Advertisement data (max size = 31 bytes, though this is
 * best kept short to conserve power while advertising) */
static uint8_t advertData[] = {
    /* Flags; this sets the device to use limited discoverable
     * mode (advertises for 30 seconds at a time) instead of general
     * discoverable mode (advertises indefinitely) */
    0x02, /* length of this data */
    SAP_GAP_ADTYPE_FLAGS, DEFAULT_DISCOVERABLE_MODE
            | SAP_GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED,

    /* Service UUID. This will notify the central device that we are
     * a BLE WiFi Provisioner  */
    0x03,
    SAP_GAP_ADTYPE_16BIT_MORE, LO_UINT16(BLE_WIFI_PROV_SERV_UUID),
    HI_UINT16(BLE_WIFI_PROV_SERV_UUID), };

/*******************************************************************************
 * @fn      AP_initServices
 *
 * @brief   Configure SNP and register services.
 *
 * @param   None.
 *
 * @return  None.
 ******************************************************************************/
static void BLE_initServices(void)
{
    uint8_t statusValue = 0;
    uint8_t blankString[] = "";

    BLE_WiFi_Profile_setParameter(BLE_WIFI_PROV_STATUS_ID, sizeof(uint8_t),
                                  &statusValue);
    BLE_WiFi_Profile_setParameter(BLE_WIFI_PROV_NOTIFY_ID, sizeof(uint8_t),
                                  &statusValue);
    BLE_WiFi_Profile_setParameter(BLE_WIFI_PROV_START_ID, sizeof(uint8_t),
                                  &statusValue);
    BLE_WiFi_Profile_setParameter(BLE_WIFI_PROV_SSID_ID, 1, blankString);
    BLE_WiFi_Profile_setParameter(BLE_WIFI_PROV_PASS_ID, 1, blankString);
    BLE_WiFi_Profile_setParameter(BLE_WIFI_PROV_DEVNAME_ID, 1, blankString);

    /* Add the SimpleProfile Service to the SNP. */
    BLE_WiFi_Profile_AddService();
    SAP_registerEventCB(BLE_processSNPEventCB, 0xFFFF);
}

//*****************************************************************************
//
//! \brief  Main application thread
//!
//! \param  none
//!
//! \return none
//!
//*****************************************************************************
void * bleThread(void *arg)
{
    SAP_Params sapParams;

    struct timespec ts;
    unsigned int prio = 0;
    uint8_t enableAdv = 1;
    uint8_t disableAdv = 0;
    uint32_t eventPend;
    uint32_t curEvent = 0;
    ble_states_t curState = BLE_IDLE; //BLE_RESET; //BLE_IDLE;
    uint32_t disconnectReason = 0;
    int32_t sblRetCode;
    SBL_Params sblParams;
    struct networkIfMsg networkMsg;

    /* Authentication parameters */
    uint32_t passkey;
    uint8_t ioCap = SAP_DISPLAY_ONLY;
    uint8_t pairMode = SAP_SECURITY_WAIT_FOR_REQUEST;
    uint8_t bonding = 1;

    sem_post(&App_CB.bleReady);
    sem_wait(&App_CB.startBarrier);

    /* Register to receive notifications from Simple Profile if characteristics
     have been written to */
    BLE_WiFi_Profile_RegisterAppCB(&bleCallbacks);

    while (1)
    {
        switch (curState)
        {
        case BLE_RESET:
        {
            /* Make sure CC26xx is not in BSL and restarting it */
            GPIO_write(Board_RESET, 0);
            GPIO_write(Board_SRDY, 1);

            usleep(10000);

            GPIO_write(Board_RESET, 1);

            Display_printf(
                    display, 0, 0,
                    "[bleThread] Initializing CC26xx BLE network processor...");

            /* Initialize UART port parameters within SAP parameters */
            SAP_initParams(SAP_PORT_REMOTE_UART, &sapParams);

            sapParams.port.remote.mrdyPinID = Board_MRDY;
            sapParams.port.remote.srdyPinID = Board_SRDY;
            sapParams.port.remote.boardID = Board_UART0;

            /* Setup NP module */
            SAP_open(&sapParams);

            /* Register Application thread's callback to receive
             asynchronous requests from the NP. */
            SAP_setAsyncCB(BLE_asyncCB);

            /* Reset the NP, and await a power-up indication.
             * Clear any pending power indications received prior to this
             * reset call
             */
            clock_gettime(CLOCK_REALTIME, &ts);
            ts.tv_sec += 1;
            mq_timedreceive(bleQueueRec, (void*) &curEvent, sizeof(uint32_t),
                            &prio, &ts);

            SAP_reset();

            curEvent = 0;

            mq_receive(bleQueueRec, (void*) &curEvent, sizeof(uint32_t), &prio);

            if (curEvent == BLE_EVT_BSL_BUTTON)
            {
                curState = BLE_SBL;
            }
            else if (curEvent == BLE_EVT_PUI)
            {
                Display_printf(display, 0, 0,
                               "[bleThread] Initialized BLE SNP!");

                /* Read BD ADDR */
                SAP_setParam(SAP_PARAM_HCI, SNP_HCI_OPCODE_READ_BDADDR, 0,
                NULL);

                /* Setup Services - Service creation is blocking so no need
                 * to pend */
                BLE_initServices();
                curState = BLE_IDLE;

                /* Sending signal to network thread that BLE powered-up successfully */
                networkMsg.msg = NETWORK_MSG_BLE_READY;
                mq_send(App_CB.networkIfQueue, (char *) &networkMsg,
                        sizeof(struct networkIfMsg), NULL);
            }
            else
            {
                Display_printf(display, 0, 0,
                               "[bleThread] Warning! Unexpected Event %lu",
                               eventPend);
                continue;
            }
        }
            break;
        case BLE_START_ADV:
        {
            /* Turn on user LED to indicate advertising */
            GPIO_write(Board_LED0, Board_LED_ON);

            Display_printf(display, 0, 0,
                           "[bleThread] Starting advertisement... ");

            /* Setting Advertising Name */
            SAP_setServiceParam(SNP_GGS_SERV_ID, SNP_GGS_DEVICE_NAME_ATT,
                                sizeof(snpDeviceName), snpDeviceName);

            /* Set advertising data. */
            SAP_setParam(SAP_PARAM_ADV, SAP_ADV_DATA_NOTCONN,
                         sizeof(advertData), advertData);

            /* Set scan response data. */
            SAP_setParam(SAP_PARAM_ADV, SAP_ADV_DATA_SCANRSP,
                         sizeof(scanRspData), scanRspData);

            /* Enable Advertising and await NP response */
            SAP_setParam(SAP_PARAM_ADV, SAP_ADV_STATE, 1, &enableAdv);

            do
            {
                curEvent = 0;
                mq_receive(bleQueueRec, (void*) &curEvent, sizeof(uint32_t),
                           &prio);

                if (curEvent != BLE_EVT_ADV_ENB)
                {
                    Display_printf(display, 0, 0,
                                   "[bleThread] Warning! Unexpected Event %lu",
                                   curEvent);
                }
            }
            while (curEvent != BLE_EVT_ADV_ENB);

            Display_printf(display, 0, 0,
                           "[bleThread] Advertisement started!");
            Display_printf(
                    display, 0, 0,
                    "[bleThread] Waiting for connection (or timeout)... ");

            /* Wait for connection or button press to cancel advertisement */
            clock_gettime(CLOCK_REALTIME, &ts);
            ts.tv_sec += 30;
            curEvent = 0;
            mq_timedreceive(bleQueueRec, (void*) &curEvent, sizeof(uint32_t),
                            &prio, &ts);

            Display_printf(display, 0, 0,
                           "[bleThread] BLE Start Advertisement. Event %lu",
                           curEvent);

            if (curEvent == BLE_EVT_CONN_EST)
            {
                GPIO_disableInt(Board_GPIO_BUTTON1);
                curState = BLE_CONNECTED;
            }
            else if (curEvent == BLE_EVT_BSL_BUTTON)
            {
                curState = BLE_SBL;
                break;
            }
            else if (curEvent == BLE_EVT_SWITCH_PROV)
            {
                Display_printf(
                        display, 0, 0,
                        "[bleThread] Switching to AP provisioning mode!");
                curState = BLE_CANCEL_ADV;
                break;
            }
            else if(curEvent == BLE_EVT_ADV_END)
            {
                Display_printf(display, 0, 0,
                        "[bleThread] BLE advertisement canceled by user!");
                curState = BLE_CANCEL_ADV;
            }
            else
            {

                Display_printf(display, 0, 0,
                               "[bleThread] Advertisement Timeout!");
                disconnectReason = WIFI_EVT_BLE_TIME;
                curState = BLE_CANCEL_ADV;
            }
        }
            break;

        case BLE_CONNECTED:
        {
            /* Before connecting, NP will send the stop ADV message */
            do
            {
                curEvent = 0;
                mq_receive(bleQueueRec, (void*) &curEvent, sizeof(uint32_t),
                           &prio);

                if (curEvent != BLE_EVT_ADV_END)
                {
                    Display_printf(display, 0, 0,
                                   "[bleThread] Warning! Unexpected Event %lu",
                                   eventPend);
                }
            }
            while (curEvent != BLE_EVT_ADV_END);

            /* Update State and Characteristic values on LCD */
            Display_printf(display, 0, 0,
                           "[bleThread]  Peer connected! (%s)", peerstr);

            /* Set IO capabilities. This device only has display capabilities,
             * so it will display the passkey during pairing */
            SAP_setParam(SAP_PARAM_SECURITY, SAP_SECURITY_IOCAPS, 1, &ioCap);

            /* Set pairing mode to wait for a request to pair */
            SAP_setParam(SAP_PARAM_SECURITY, SAP_SECURITY_BEHAVIOR, 1,
                         &pairMode);

            /* Request bonding so long-term keys for re-encryption upon
             * subsequent connections without repairing */
            SAP_setParam(SAP_PARAM_SECURITY, SAP_SECURITY_BONDING, 1, &bonding);

            /* Send security parameters to central device */
            SAP_sendSecurityRequest();

            /* Wait to receive security event from central device indicating if
             * peripheral needs to pair or is already paired */
            do
            {
                curEvent = 0;
                mq_receive(bleQueueRec, (void*) &curEvent, sizeof(uint32_t),
                           &prio);

                if (curEvent != BLE_EVT_START_PAIR && curEvent != BLE_EVT_PAIRED)
                {
                    Display_printf(display, 0, 0,
                                   "[bleThread] Warning! Unexpected Event %lu",
                                   eventPend);
                }
            }
            while (curEvent != BLE_EVT_START_PAIR && curEvent != BLE_EVT_PAIRED);

            /* Peripheral is not bonded, so start pairing process */
            if (curEvent == BLE_EVT_START_PAIR)
            {
                /* Generate random passkey */
                passkey = SAP_getRand() % 1000000;

                /* Set the passkey that must be entered on the central
                 * device for authentication  */
                SAP_setAuthenticationRsp(passkey);

                /* Display passkey to user */
                Display_printf(
                        display, 0, 0,
                        "[bleThread] Please enter the passkey on your device:");
                Display_printf(display, 0, 0, "[bleThread] %lu", passkey);

                /* Wait to receive event from central device indicating
                 * if pairing was a success */
                curEvent = 0;
                mq_receive(bleQueueRec, (void*) &curEvent, sizeof(uint32_t),
                           &prio);

                if (curEvent == BLE_EVT_PAIRED)
                {
                    curState = BLE_PAIRED;
                    Display_printf(
                            display, 0, 0,
                            "[bleThread] Device is successfully paired!");
                }
                else if (curEvent == BLE_EVT_WRONG_PASSKEY)
                {
                    /* Disconnect the device and restart advertising */
                    SAP_setParam(SAP_PARAM_CONN, SAP_CONN_STATE,
                                 sizeof(connHandle), (uint8_t *) &connHandle);
                    curState = BLE_START_ADV;
                    Display_printf(
                            display,
                            0,
                            0,
                            "[bleThread] Wrong passkey entered. "
                            "Reconnect and try again.");
                }
                else if (curEvent == BLE_EVT_CONN_TERM)
                {
                    /* Client has disconnected from server */
                    SAP_setParam(SAP_PARAM_CONN, SAP_CONN_STATE,
                                 sizeof(connHandle), (uint8_t *) &connHandle);
                    curState = BLE_START_ADV;
                    Display_printf(display, 0, 0,
                                   "[bleThread] Disconnected from device.");
                }
                else
                {
                    Display_printf(display, 0, 0,
                                   "[bleThread] Warning! Unexpected Event %lu",
                                   eventPend);
                }

            }
            else if (curEvent == BLE_EVT_PAIRED)
            {
                /* Peripheral is already bonded */
                Display_printf(display, 0, 0,
                               "[bleThread] Device is successfully paired!");
                curState = BLE_PAIRED;
            }

        }
            break;

        case BLE_PAIRED:
        {
            curEvent = 0;
            mq_receive(bleQueueRec, (void*) &curEvent, sizeof(uint32_t), &prio);

            if (curEvent == BLE_EVT_START_PROVISION)
            {
                disconnectReason = WIFI_EVT_BLE_PROV_START;
            }
            else if (curEvent == BLE_EVT_PROV_COMPLETE)
            {
                disconnectReason = WIFI_EVT_BLE_PROV_COMPLETE;
            }
            else
            {
                disconnectReason = WIFI_EVT_BLE_TIME;
            }

            /* Client has disconnected from server */
            SAP_setParam(SAP_PARAM_CONN, SAP_CONN_STATE, sizeof(connHandle),
                         (uint8_t *) &connHandle);

            do
            {
                curEvent = 0;
                mq_receive(bleQueueRec, (void*) &curEvent, sizeof(uint32_t),
                           &prio);

                if (curEvent != BLE_EVT_CONN_TERM)
                {
                    Display_printf(display, 0, 0,
                                   "[bleThread] Warning! Unexpected Event %lu",
                                   eventPend);
                }
            }
            while (curEvent != BLE_EVT_CONN_TERM);

            curState = BLE_CANCEL_ADV;
        }
            break;

        case BLE_CANCEL_ADV:
        {
            Display_printf(display, 0, 0,
                           "[bleThread] Advertisement has been canceled!");

            /* Cancel Advertisement */
            SAP_setParam(SAP_PARAM_ADV, SAP_ADV_STATE, 1, &disableAdv);

            do
            {
                curEvent = 0;
                mq_receive(bleQueueRec, (void*) &curEvent, sizeof(uint32_t),
                           &prio);

            }
            while (curEvent != BLE_EVT_ADV_END);

            /* Checking to see if we have anything we want to communicate to
             * the Wi-Fi thread.
             */
            if (disconnectReason != 0)
            {
                /* Signal to network thread */
                if(disconnectReason == WIFI_EVT_BLE_PROV_START)
                {
                    /* Credentials need to be tested by network thread */
                    networkMsg.msg = NETWORK_MSG_TEST_CREDENTIALS;
                    mq_send(App_CB.networkIfQueue, (char *) &networkMsg,
                            sizeof(struct networkIfMsg), NULL);
                }
                else if(disconnectReason == WIFI_EVT_BLE_PROV_COMPLETE)
                {
                    /* Provisioning is complete */
                    SAP_close();
                    GPIO_write(Board_SRDY, 1);
                    GPIO_write(Board_RESET, 0);

                    networkMsg.msg = NETWORK_MSG_BLE_PROV_DONE;
                    mq_send(App_CB.networkIfQueue, (char *) &networkMsg,
                            sizeof(struct networkIfMsg), NULL);
                }
                else if(disconnectReason == WIFI_EVT_BLE_TIME)
                {
                    /* Connection timed out */
                    SAP_close();
                    GPIO_write(Board_SRDY, 1);
                    GPIO_write(Board_RESET, 0);

                    CLR_STATUS_BIT(App_CB.status, AppStatusBits_BLEProvInProgress);
                    networkMsg.msg = NETWORK_MSG_PROVISION_START_STOP;
                    mq_send(App_CB.networkIfQueue, (char *) &networkMsg,
                                                sizeof(struct networkIfMsg), NULL);
                }

                disconnectReason = 0;
            }
            else
            {
                /* User requested to stop BLE provisioning */
                SAP_close();
                GPIO_write(Board_SRDY, 1);
                GPIO_write(Board_RESET, 0);
            }
            curState = BLE_IDLE;
        }
            break;

        case BLE_IDLE:
        {
            /* Turn off user LED to indicate stop advertising */
            Display_printf(display, 0, 0, "[bleThread] State set to idle.");

            /* Key Press triggers state change from idle */
            mq_receive(bleQueueRec, (void*) &curEvent, sizeof(uint32_t), &prio);

            if (curEvent == BLE_EVT_BSL_BUTTON)
            {
                curState = BLE_SBL;
            }
            else if (curEvent == BLE_EVT_START_PROVISION
                    || curEvent == BLE_EVT_SWITCH_PROV)
            {
                curState = BLE_START_ADV;
            }
            else
            {
                curState = BLE_RESET;
            }
        }
            break;

        case BLE_SBL:
        {
            Display_printf(display, 0, 0,
                           "[bleThread] Device being set into BSL mode.");

            /* Close NP so SBL can use serial port */
            SAP_close();

            GPIO_disableInt(Board_SRDY);
            /* Initialize SBL parameters and open port to target device */
            SBL_initParams(&sblParams);
            sblParams.resetPinID = Board_RESET;
            sblParams.blPinID = Board_MRDY;
            sblParams.targetInterface = SBL_DEV_INTERFACE_UART;
            sblParams.localInterfaceID = Board_UART0;

            Display_printf(display, 0, 0,
                           "[bleThread] Programming the CC26xx... ");

            sblRetCode = WiFi_SNP_updateFirmware(&sblParams);

            if (sblRetCode != WIFI_SNP_SBL_SUCCESS)
            {
                Display_printf(
                        display, 0, 0,
                        "[bleThread] ERROR: Cannot program SNP image (Code %d)",
                        sblRetCode);
            }
            else
            {
                Display_printf(
                        display, 0, 0,
                        "[bleThread] SNP image programmed successfully.");
            }

            Display_printf(display, 0, 0, "[bleThread] Resetting device.");

            /* Regardless of successful write we must restart the SNP
             force reset */
            PRCMMCUReset(true);
        }
            break;
        }
    }
}

//*****************************************************************************
//                 BLE Callbacks
//*****************************************************************************
/*
 * This is a callback operating in the NPI task.
 * These are events this application has registered for.
 */
static void BLE_processSNPEventCB(uint16_t event, snpEventParam_t *param)
{
    uint32_t eventPend;

    switch (event)
    {
    case SNP_CONN_EST_EVT:
    {
        snpConnEstEvt_t * connEstEvt = (snpConnEstEvt_t *) param;

        /* Update Peer Addr String */
        connHandle = connEstEvt->connHandle;
        ProfileUtil_convertBdAddr2Str(&peerstr[peerstrIDX], connEstEvt->pAddr);

        /* Notify state machine of established connection */
        eventPend = BLE_EVT_CONN_EST;
        mq_send(bleQueueSend, (void*) &eventPend, sizeof(uint32_t), 1);
    }
        break;

    case SNP_CONN_TERM_EVT:
    {
        connHandle = BLE_DEFAULT_CONN_HANDLE;

        /* Notify state machine of disconnection event */
        eventPend = BLE_EVT_CONN_TERM;
        mq_send(bleQueueSend, (void*) &eventPend, sizeof(uint32_t), 1);
    }
        break;

    case SNP_ADV_STARTED_EVT:
    {
        snpAdvStatusEvt_t *advEvt = (snpAdvStatusEvt_t *) param;
        if (advEvt->status == SNP_SUCCESS)
        {
            /* Notify state machine of Advertisement Enabled */
            eventPend = BLE_EVT_ADV_ENB;
            mq_send(bleQueueSend, (void*) &eventPend, sizeof(uint32_t), 1);
        }
        else
        {
            eventPend = BLE_EVT_ERROR;
            mq_send(bleQueueSend, (void*) &eventPend, sizeof(uint32_t), 1);
        }
    }
        break;

    case SNP_ADV_ENDED_EVT:
    {
        snpAdvStatusEvt_t * advEvt = (snpAdvStatusEvt_t *) param;
        if (advEvt->status == SNP_SUCCESS)
        {
            /* Notify state machine of Advertisement Disabled */
            eventPend = BLE_EVT_ADV_END;
            mq_send(bleQueueSend, (void*) &eventPend, sizeof(uint32_t), 1);
        }
    }
        break;
    case SNP_SECURITY_EVT:
    {
        snpSecurityEvt_t * secEvt = (snpSecurityEvt_t *) param;

        /* Check if peripheral is already bonded
         * or if authentication was a success */
        if (secEvt->state == SNP_GAPBOND_PAIRING_STATE_BONDED
                || (secEvt->state == SNP_GAPBOND_PAIRING_STATE_COMPLETE
                        && secEvt->status == SNP_SUCCESS))
        {
            eventPend = BLE_EVT_PAIRED;
            mq_send(bleQueueSend, (void*) &eventPend, sizeof(uint32_t), 1);
        }
        /* Wrong passkey was entered */
        else if (secEvt->status == SNP_PAIRING_FAILED_CONFIRM_VALUE)
        {
            eventPend = BLE_EVT_WRONG_PASSKEY;
            mq_send(bleQueueSend, (void*) &eventPend, sizeof(uint32_t), 1);
        }

    }
        break;

    case SNP_AUTHENTICATION_EVT:
    {
        snpAuthenticationEvt_t * authEvt = (snpAuthenticationEvt_t *) param;
        if (authEvt->display == 1)
        {
            eventPend = BLE_EVT_START_PAIR;
            mq_send(bleQueueSend, (void*) &eventPend, sizeof(uint32_t), 1);
        }
    }
        break;

    default:
        break;
    }
}

static void BLE_SPWriteCB(uint8_t charID)
{
    uint8_t startValue;
    uint32_t eventPend;

    switch (PROFILE_ID_CHAR(charID))
    {
    case BLE_WIFI_PROV_STATUS_CHAR:
        switch (PROFILE_ID_CHARTYPE(charID))
        {
        case PROFILE_VALUE:
            break;
        default:
            /* Should not receive other types */
            break;
        }
        break;
    case BLE_WIFI_PROV_SSID_CHAR:
        switch (PROFILE_ID_CHARTYPE(charID))
        {
        case PROFILE_VALUE:
            break;
        default:
            /* Should not receive other types */
            break;
        }
        break;
    case BLE_WIFI_PROV_PASS_CHAR:
        switch (PROFILE_ID_CHARTYPE(charID))
        {
        case PROFILE_VALUE:
            break;
        default:
            /* Should not receive other types */
            break;
        }
        break;
    case BLE_WIFI_PROV_DEVNAME_CHAR:
        switch (PROFILE_ID_CHARTYPE(charID))
        {
        case PROFILE_VALUE:
            break;
        default:
            /* Should not receive other types */
            break;
        }
        break;
    case BLE_WIFI_PROV_START_CHAR:
        switch (PROFILE_ID_CHARTYPE(charID))
        {
        case PROFILE_VALUE:
            BLE_WiFi_Profile_getParameter(BLE_WIFI_PROV_START_ID, &startValue);
            if (startValue == BLE_START_PROVISION)
            {
                eventPend = BLE_EVT_START_PROVISION;
                mq_send(bleQueueSend, (void*) &eventPend, sizeof(uint32_t), 1);
            }
            else if (startValue == BLE_COMPLETE_PROVISION)
            {
                eventPend = BLE_EVT_PROV_COMPLETE;
                mq_send(bleQueueSend, (void*) &eventPend, sizeof(uint32_t), 1);
            }
            break;
        default:
            /* Should not receive other types */
            break;
        }
        break;
    default:
        /* Other Characteristics not writable */
        break;
    }
}

static void BLE_SPcccdCB(uint8_t charID, uint16_t value)
{
    switch (PROFILE_ID_CHAR(charID))
    {
    case BLE_WIFI_PROV_NOTIFY_CHAR:
        switch (PROFILE_ID_CHARTYPE(charID))
        {
        case PROFILE_CCCD:
            break;

        default:
            /* Should not receive other types */
            break;
        }
        break;

    default:
        break;
    }
}

/*******************************************************************************
 * This is a callback operating in the NPI task.
 * These are Asynchronous indications.
 ******************************************************************************/
static void BLE_asyncCB(uint8_t cmd1, void *pParams)
{
    uint32_t eventPend;

    switch (SNP_GET_OPCODE_HDR_CMD1(cmd1))
    {
    case SNP_DEVICE_GRP:
    {
        switch (cmd1)
        {
        case SNP_POWER_UP_IND:
            /* Notify state machine of Power Up Indication */
            eventPend = BLE_EVT_PUI;
            mq_send(bleQueueSend, (void*) &eventPend, sizeof(uint32_t), 1);
            break;

        case SNP_HCI_CMD_RSP:
        {
            snpHciCmdRsp_t *hciRsp = (snpHciCmdRsp_t *) pParams;
            switch (hciRsp->opcode)
            {
            case SNP_HCI_OPCODE_READ_BDADDR:
                /* Update NWP Addr String */
                ProfileUtil_convertBdAddr2Str(&nwpstr[nwpstrIDX],
                                              hciRsp->pData);
                break;
            default:
                break;
            }
        }
            break;

        case SNP_EVENT_IND:
            /* Notify state machine of Advertisement Enabled */
            eventPend = BLE_EVT_ADV_ENB;
            mq_send(bleQueueSend, (void*) &eventPend, sizeof(uint32_t), 1);
            break;

        default:
            break;
        }
    }
        break;

    default:
        break;
    }
}
